//---------------------------------------------------------------------
// File: ExceptionShielding.cs
// 
// Summary: Provides Exception Shielding methods to generate either 
//          SOAPExceptions or custom WCF fault exceptions.  Used from
//          either ASMX or WCF based web services.
//
//
//---------------------------------------------------------------------
// This file is part of the Microsoft ESB Guidance for BizTalk
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This source code is intended only as a supplement to Microsoft BizTalk
// Server 2006 R2 release and/or on-line documentation. See these other
// materials for detailed information regarding Microsoft code samples.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using Microsoft.Practices.ESB.Exception.Management.FaultContracts;
using System.Reflection;
using System.Collections;
using System.Diagnostics;
using System.Web.Services.Protocols;
using System.Web.Services;
using System.Globalization;

namespace Microsoft.Practices.ESB.Exception.Management
{
    public class ExceptionShielding
    {
        /// <summary>
        /// Called from the Catch block of WCF services.  May pass in as a generic a Fault 
        /// Contract that derives from ServiceFault.  This is used to pass a santized Fault 
        /// back to the client, shielding them from the details of the exception.  This
        /// will also log the exception to the event log.
        /// </summary>
        /// <typeparam name="T">Fault Contract type to return</typeparam>
        /// <param name="ex">System.Exception caught by Catch</param>
        /// <param name="eventId">Event Id to log</param>
        /// <param name="message">Generic message for Fault to return</param>
        /// <returns>Throws fault exception of type 'T'</returns>
        public static FaultException<T> CreateFaultException<T>(System.Exception ex, 
            EventLogger.EventId eventId, string message)
            where T : ServiceFault, new()
        {
            
            StackFrame stackFrame = new StackFrame(1);
            string methodName = stackFrame.GetMethod().Name;
            Guid guid = Guid.NewGuid();
            message = message.Trim();

            // Get the fault type and set the error id and message
            T serviceFault = new T();
            Type type = serviceFault.GetType();
            type.InvokeMember("SetFault", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, serviceFault, new object[] { guid, message });

            // log and throw new error
            EventLogger.LogMessage(EventFormatter.FormatEvent(stackFrame, ex, guid),
                    EventLogEntryType.Error, (int)eventId);

            return new FaultException<T>(serviceFault, new FaultReason(Exceptions.GetErrorDescription(Exceptions.FaultExceptionMessage,methodName)));
        }

        /// <summary>
        /// Called from the Catch block of ASMX services.  This is used to pass a santized  
        /// SOAP Exception back to the client, shielding them from the details of the exception.  This
        /// will also log the exception to the event log.
        /// </summary>
        /// <param name="ex">System.Exception caught by Catch</param>
        /// <param name="eventId">Event Id to log</param>
        /// <param name="message">Generic message for Fault to return</param>
        /// <returns>Throws SOAP Exception</returns>
        public static SoapException CreateSoapException(System.Exception ex,
            EventLogger.EventId eventId, string message)
        {

            StackFrame stackFrame = new StackFrame(1);
            string methodName = stackFrame.GetMethod().Name;
            Guid guid = Guid.NewGuid();
            message = string.Format(CultureInfo.CurrentCulture, "{0} : Error Id = {1}", message.Trim(), guid.ToString());

            // Get the service error type and set the error id and message
            ServiceException serviceEx = new ServiceException(message);

            // log and throw new error
            EventLogger.LogMessage(EventFormatter.FormatEvent(stackFrame, ex, guid),
                    EventLogEntryType.Error, (int)eventId);

            return new SoapException(Exceptions.GetErrorDescription(Exceptions.SoapExceptionMessage,methodName) , SoapException.ServerFaultCode, serviceEx);
        }
    }

}
